home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / capvumeter.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  20.1 KB  |  710 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19. #include <crtdbg.h>
  20. #include <math.h>
  21. #include <windows.h>
  22. #include <commctrl.h>
  23. #include <vfw.h>
  24.  
  25. #include "resource.h"
  26. #include "oshelper.h"
  27. #include "helpfile.h"
  28. #include "MonoBitmap.h"
  29. #include "FHT.h"
  30. #include "Error.h"
  31.  
  32.  
  33. #define BALANCE_DEADZONE        (2048)
  34.  
  35. ///////////////////////////////////////////////////////////////////////////
  36. //
  37. //    Volume meter
  38. //
  39. ///////////////////////////////////////////////////////////////////////////
  40.  
  41. enum {
  42.     VMMODE_VUMETER,
  43.     VMMODE_SCOPE,
  44.     VMMODE_ANALYZER,
  45. };
  46.  
  47. typedef struct VumeterDlgData {
  48.     RECT rect[2];
  49.     LONG last[2], peak[2];
  50.     void *buffer;
  51.     char *scrollBuffer;
  52.     int scrollSize;
  53.     int bufCnt;
  54.     WAVEFORMATEX wfex;
  55.     HWAVEIN hWaveIn;
  56.     WAVEHDR bufs[2];
  57.     char *pBitmap;
  58.     MonoBitmap *pbm;
  59.     int mode;
  60.     Fht *pfht_left, *pfht_right;
  61.     HWND hwndVolume, hwndBalance;
  62.     long lVolume, lBalance;
  63.     MIXERCONTROLDETAILS mcdVolume;
  64.     MIXERCONTROLDETAILS_UNSIGNED mcdVolumeData[2];
  65.  
  66.     BOOL fOpened;
  67.     BOOL fRecording;
  68.     BOOL fStereo;
  69.     BOOL fOddByte;
  70. } VumeterDlgData;
  71.  
  72. static void CaptureVumeterDestruct(VumeterDlgData *vdd) {
  73.     if (vdd->fRecording) waveInReset(vdd->hWaveIn);
  74.  
  75.     while(vdd->bufCnt--) {
  76.         --vdd->bufCnt;
  77.         waveInUnprepareHeader(vdd->hWaveIn, &vdd->bufs[vdd->bufCnt], sizeof(WAVEHDR));
  78.     }
  79.     GdiFlush();
  80.     delete vdd->pbm;
  81.     delete vdd->pfht_left;
  82.     delete vdd->pfht_right;
  83.     if (vdd->fOpened) waveInClose(vdd->hWaveIn);
  84.     if (vdd->buffer) freemem(vdd->buffer);
  85.     freemem(vdd);
  86. }
  87.  
  88. static void CaptureVumeterRepaintVumeter(VumeterDlgData *vdd, HDC hDC) {
  89.     HBRUSH hbr, hbrp;
  90.     int i;
  91.  
  92.     for(i=0; i<(vdd->fStereo?2:1); i++)
  93.         Draw3DRect(hDC,
  94.                 vdd->rect[i].left,
  95.                 vdd->rect[i].top,
  96.                 vdd->rect[i].right  - vdd->rect[i].left,
  97.                 vdd->rect[i].bottom - vdd->rect[i].top,
  98.                 TRUE);
  99.  
  100.     if ((hbr = CreateSolidBrush(RGB(0x00,0x00,0xff))) && (hbrp = CreateSolidBrush(RGB(0xff,0x00,0x00)))) {
  101.         RECT r;
  102.  
  103.         for(i=0; i<(vdd->fStereo?2:1); i++) {
  104.             r.top        = vdd->rect[i].top+1;
  105.             r.bottom    = vdd->rect[i].bottom-1;
  106.  
  107.             if (vdd->peak[i]) {
  108.                 r.left        = vdd->rect[i].left+1 + vdd->last[i];
  109.                 r.right        = vdd->rect[i].left+1 + vdd->peak[i];
  110.                 FillRect(hDC, &r, hbrp);
  111.             }
  112.  
  113.             if (vdd->last[i]) {
  114.                 r.left        = vdd->rect[i].left+1;
  115.                 r.right        = vdd->rect[i].left+1 + vdd->last[i];
  116.                 FillRect(hDC, &r, hbr);
  117.             }
  118.  
  119.             r.left        = vdd->rect[i].left+1 + vdd->peak[i];
  120.             r.right        = vdd->rect[i].right-1;
  121.             FillRect(hDC, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
  122.         }
  123.  
  124.     }
  125.     if (hbr) DeleteObject(hbr);
  126.     if (hbrp) DeleteObject(hbrp);
  127. }
  128.  
  129. static void CaptureVumeterDoVumeter(VumeterDlgData *vdd, HDC hdc, unsigned long *total, unsigned long *peak, DWORD dwBytes) {
  130.     HBRUSH hbr = CreateSolidBrush(RGB(0x00,0x00,0xff));
  131.     HBRUSH hbrp = CreateSolidBrush(RGB(0xff,0x00,0x00));
  132.     LONG lvl, plvl;
  133.     RECT r;
  134.     int i;
  135.  
  136.     for(i=0; i<(vdd->fStereo?2:1); i++) {
  137.         lvl = MulDiv(total[i],vdd->rect[i].right-vdd->rect[i].left-2,dwBytes*128);
  138.         plvl = MulDiv(peak[i],vdd->rect[i].right-vdd->rect[i].left-2,128);
  139.  
  140.         r.top        = vdd->rect[i].top+1;
  141.         r.bottom    = vdd->rect[i].bottom-1;
  142.  
  143.         if (lvl < vdd->last[i] && hbrp) {
  144.             r.left        = vdd->rect[i].left+1 + max(lvl, plvl);
  145.             r.right        = vdd->rect[i].left+1 + min(vdd->peak[i], vdd->last[i]);
  146.             FillRect(hdc, &r, hbrp);
  147.         } else if (lvl > vdd->last[i] && hbr) {
  148.             r.left        = vdd->rect[i].left+1 + vdd->last[i];
  149.             r.right        = vdd->rect[i].left+1 + min(lvl, plvl);
  150.             FillRect(hdc, &r, hbr);
  151.         }
  152.  
  153.         if (plvl < vdd->peak[i]) {
  154.             r.left        = vdd->rect[i].left+1 + plvl;
  155.             r.right        = vdd->rect[i].left+1 + vdd->peak[i];
  156.             FillRect(hdc, &r, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
  157.         } else if (plvl > vdd->peak[i] && hbrp) {
  158.             r.left        = vdd->rect[i].left+1 + max(lvl, vdd->peak[i]);
  159.             r.right        = vdd->rect[i].left+1 + plvl;
  160.             FillRect(hdc, &r, hbrp);
  161.         }
  162.  
  163.         vdd->last[i] = lvl;
  164.         vdd->peak[i] = plvl;
  165.     }
  166.  
  167.     if (hbr) DeleteObject(hbr);
  168.     if (hbrp) DeleteObject(hbrp);
  169. }
  170.  
  171. static void CaptureVumeterRepaintScopeAnalyzer(VumeterDlgData *vdd, HDC hDC) {
  172.     vdd->pbm->BitBlt(hDC, vdd->rect[0].left, vdd->rect[0].top, 0, 0, vdd->rect[0].right-vdd->rect[0].left, vdd->rect[0].bottom-vdd->rect[0].top);
  173.     if (vdd->fStereo)
  174.         vdd->pbm->BitBlt(hDC, vdd->rect[1].left, vdd->rect[1].top, 0, vdd->rect[0].bottom-vdd->rect[0].top, vdd->rect[1].right-vdd->rect[1].left, vdd->rect[1].bottom-vdd->rect[1].top);
  175. }
  176.  
  177. static void CaptureVumeterDoScope(VumeterDlgData *vdd, HDC hDC) {
  178.     int w = vdd->rect[0].right - vdd->rect[0].left;
  179.     if (w > 4096) w = 4096;
  180.  
  181.     unsigned char *src = (unsigned char *)vdd->scrollBuffer + vdd->scrollSize - (vdd->fStereo ? w*2 : w);
  182.     unsigned char *dst_col = (unsigned char *)vdd->pbm->getBits(), *dst;
  183.     int hl = vdd->rect[0].bottom - vdd->rect[0].top;
  184.     int hr = vdd->rect[1].bottom - vdd->rect[1].top;
  185.     int h1, h2, h;
  186.     unsigned char mask, c;
  187.     int iPitch = vdd->pbm->getPitch();
  188.     int left_offset = hl*iPitch;
  189.  
  190.     GdiFlush();
  191.     vdd->pbm->Clear();
  192.  
  193.     mask = 0x80;
  194.     do {
  195.         c = *src++;
  196.  
  197.         h1 = (c * hl) >> 8;
  198.         h2 = hl>>1;
  199.  
  200.         if (h1 < h2)
  201.             h = h2+1-h1;
  202.         else {
  203.             h = h1+1-h2;
  204.             h1 = h2;
  205.         }
  206.  
  207.         dst = dst_col + left_offset + iPitch * h1;
  208.         do {
  209.             *dst |= mask;
  210.             dst += iPitch;
  211.         } while(--h);
  212.  
  213.         if (vdd->fStereo) {
  214.             c = *src++;
  215.  
  216.             h1 = (c * hr) >> 8;
  217.             h2 = hr>>1;
  218.  
  219.             if (h1 < h2)
  220.                 h = h2+1-h1;
  221.             else {
  222.                 h = h1+1-h2;
  223.                 h1 = h2;
  224.             }
  225.  
  226.             dst = dst_col + iPitch * h1;
  227.             do {
  228.                 *dst |= mask;
  229.                 dst += iPitch;
  230.             } while(--h);
  231.         }
  232.  
  233.         if (!(mask>>=1)) {
  234.             mask = 0x80;
  235.             ++dst_col;
  236.         }
  237.     } while(--w);
  238.  
  239.     CaptureVumeterRepaintScopeAnalyzer(vdd, hDC);
  240. }
  241.  
  242. static void CaptureVumeterDoAnalyzer(VumeterDlgData *vdd, HDC hDC) {
  243.     int w = vdd->rect[0].right - vdd->rect[0].left;
  244.     if (w > 1024) w = 1024;
  245.  
  246.     unsigned char *src = (unsigned char *)vdd->scrollBuffer + vdd->scrollSize - (vdd->fStereo ? w*2 : w);
  247.     unsigned char *dst_col = (unsigned char *)vdd->pbm->getBits(), *dst;
  248.     int hl = vdd->rect[0].bottom - vdd->rect[0].top;
  249.     int hr = vdd->rect[1].bottom - vdd->rect[1].top;
  250.     int h1;
  251.     int x=0;
  252.     unsigned char mask;
  253.     int iPitch = vdd->pbm->getPitch();
  254.     int left_offset = hl*iPitch;
  255.  
  256.     if (vdd->fStereo) {
  257.         vdd->pfht_left->CopyInStereo8((unsigned char *)vdd->scrollBuffer, 1024);
  258.         vdd->pfht_left->Transform(w);
  259.         vdd->pfht_right->CopyInStereo8((unsigned char *)vdd->scrollBuffer + 1, 1024);
  260.         vdd->pfht_right->Transform(w);
  261.     } else {
  262.         vdd->pfht_left->CopyInMono8((unsigned char *)vdd->scrollBuffer, 1024);
  263.         vdd->pfht_left->Transform(w);
  264.     }
  265.  
  266.     GdiFlush();
  267.     vdd->pbm->Clear();
  268.  
  269.     mask = 0x80;
  270.     do {
  271.         h1 = floor(0.5 + vdd->pfht_left->GetIntensity(x) * 8 * hl);
  272.         if (h1 > hl) h1 = hl;
  273.  
  274.         if (h1>0) {
  275.             dst = dst_col + left_offset;
  276.             do {
  277.                 *dst |= mask;
  278.                 dst += iPitch;
  279.             } while(--h1);
  280.         }
  281.  
  282.         if (vdd->fStereo) {
  283.             h1 = floor(0.5 + vdd->pfht_right->GetIntensity(x) * 8 * hr);
  284.             if (h1 > hr) h1 = hr;
  285.  
  286.             if (h1>0) {
  287.                 dst = dst_col;
  288.                 do {
  289.                     *dst |= mask;
  290.                     dst += iPitch;
  291.                 } while(--h1);
  292.             }
  293.         }
  294.  
  295.         ++x;
  296.  
  297.         if (!(mask>>=1)) {
  298.             mask = 0x80;
  299.             ++dst_col;
  300.         }
  301.     } while(--w);
  302.  
  303.     CaptureVumeterRepaintScopeAnalyzer(vdd, hDC);
  304. }
  305.  
  306. static void CaptureVumeterSetupMixer(HWND hdlg, VumeterDlgData *vdd) {
  307.     MMRESULT res;
  308.     UINT mixerID;
  309.     MIXERCAPS mixerCaps;
  310.     MIXERLINE mainMixerLine;
  311.     MIXERLINE lineinMixerLine;
  312.     MIXERLINECONTROLS mixerLineControls;
  313.     MIXERCONTROL control;
  314.     bool fVolumeOk = false;
  315.  
  316.     //
  317.     // I have only this to say:
  318.     //
  319.     //        I hate the Windows mixer architecture.
  320.     //
  321.     // We have to use this braindead code because there's no good way
  322.     // to get the mixer controls associated with a wave handle.  If you
  323.     // give the mixer functions a wave handle, you always get the direct
  324.     // line to that channel, and can't get to any of the sources...
  325.     //
  326.  
  327.     mainMixerLine.cbStruct        = sizeof(MIXERLINE);
  328.  
  329.     _RPT0(0,"CaptureVumeterSetupMixer() begin\n");
  330.  
  331.     // Find the mixer line associated with the wave input device
  332.  
  333.     if (MMSYSERR_NOERROR != (res = mixerGetID((HMIXEROBJ)vdd->hWaveIn, &mixerID, MIXER_OBJECTF_HWAVEIN)))
  334.         return;
  335.  
  336.     if (MMSYSERR_NOERROR != (res = mixerGetDevCaps(mixerID, &mixerCaps, sizeof(MIXERCAPS))))
  337.         return;
  338.  
  339.     mainMixerLine.cbStruct                    = sizeof(MIXERLINE);
  340.     mainMixerLine.Target.dwType                = MIXERLINE_TARGETTYPE_WAVEIN;
  341.     mainMixerLine.Target.wMid                = mixerCaps.wMid;
  342.     mainMixerLine.Target.wPid                = mixerCaps.wPid;
  343.     mainMixerLine.Target.vDriverVersion        = mixerCaps.vDriverVersion;
  344.     strcpy(mainMixerLine.Target.szPname,mixerCaps.szPname);
  345.  
  346.     if (MMSYSERR_NOERROR == (res = mixerGetLineInfo((HMIXEROBJ)vdd->hWaveIn, &mainMixerLine, MIXER_OBJECTF_HWAVEIN))) {
  347.         int src;
  348.  
  349.         _RPT1(0,"Found input line - %d connections\n", mainMixerLine.cConnections);
  350.  
  351.         // Look for the line-in line that feeds to us
  352.  
  353.         for(src = 0; src < mainMixerLine.cConnections; src++) {
  354.  
  355.             lineinMixerLine.cbStruct        = sizeof(MIXERLINE);
  356.             lineinMixerLine.dwDestination    = mainMixerLine.dwDestination;
  357.             lineinMixerLine.dwSource        = src;
  358.  
  359.             if (MMSYSERR_NOERROR == (res = mixerGetLineInfo((HMIXEROBJ)mixerID, &lineinMixerLine, MIXER_OBJECTF_MIXER| MIXER_GETLINEINFOF_SOURCE))
  360.                 && (lineinMixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
  361.                     || lineinMixerLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE)) {
  362.  
  363.                 _RPT0(0,"Found Line-In for this input device\n");
  364.  
  365.                 vdd->mcdVolume.paDetails = vdd->mcdVolumeData;
  366.  
  367.                 // Look for a volume control
  368.  
  369.                 mixerLineControls.cbStruct        = sizeof mixerLineControls;
  370.                 mixerLineControls.dwLineID        = lineinMixerLine.dwLineID;
  371.                 mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
  372.                 mixerLineControls.cbmxctrl        = sizeof(MIXERCONTROL);
  373.                 mixerLineControls.pamxctrl        = &control;
  374.  
  375.                 if (MMSYSERR_NOERROR == (res = mixerGetLineControls((HMIXEROBJ)mixerID, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_MIXER))) {
  376.                     _RPT0(0,"Got the control.\n");
  377.  
  378.                     vdd->mcdVolume.cbStruct            = sizeof(MIXERCONTROLDETAILS);
  379.                     vdd->mcdVolume.dwControlID        = control.dwControlID;
  380.                     vdd->mcdVolume.cChannels        = lineinMixerLine.cChannels==2 ? 2 : 1;
  381.                     vdd->mcdVolume.cMultipleItems    = 0;
  382.                     vdd->mcdVolume.cbDetails        = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
  383.  
  384.                     if (MMSYSERR_NOERROR == (res = mixerGetControlDetails((HMIXEROBJ)mixerID, &vdd->mcdVolume, MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_MIXER)))
  385.                         fVolumeOk = true;
  386.  
  387.                 }
  388.  
  389.                 break;
  390.  
  391.             }
  392.         }
  393.     }
  394.  
  395.     if (fVolumeOk) {
  396.         vdd->hwndVolume = GetDlgItem(hdlg, IDC_VOLUME);
  397.  
  398.         if (vdd->mcdVolume.cChannels > 1) {
  399.             long l, r;
  400.  
  401.             l = vdd->mcdVolumeData[0].dwValue;
  402.             r = vdd->mcdVolumeData[1].dwValue;
  403.  
  404.             vdd->lVolume = max(l,r);
  405.             if (vdd->lVolume)
  406.                 vdd->lBalance = MulDiv(r-l, 32768-BALANCE_DEADZONE, vdd->lVolume);
  407.             else
  408.                 vdd->lBalance = 0;
  409.         } else {
  410.             vdd->lVolume = vdd->mcdVolumeData[0].dwValue;
  411.             vdd->lBalance = 0;
  412.         }
  413.  
  414.         EnableWindow(vdd->hwndVolume, TRUE);
  415.         SendMessage(vdd->hwndVolume, TBM_SETRANGEMIN, TRUE, 0);
  416.         SendMessage(vdd->hwndVolume, TBM_SETRANGEMAX, TRUE, 65535);
  417.         SendMessage(vdd->hwndVolume, TBM_SETPOS, TRUE, vdd->lVolume);
  418.  
  419.         SendMessage(vdd->hwndVolume, TBM_SETTICFREQ, 8192, 0);
  420.  
  421.         EnableWindow(GetDlgItem(hdlg, IDC_STATIC_VOLUME), TRUE);
  422.  
  423.         if (vdd->mcdVolume.cChannels>1) {
  424.             vdd->hwndBalance = GetDlgItem(hdlg, IDC_BALANCE);
  425.  
  426.             EnableWindow(vdd->hwndBalance, TRUE);
  427.             SendMessage(vdd->hwndBalance, TBM_SETRANGEMIN, TRUE, 0);
  428.             SendMessage(vdd->hwndBalance, TBM_SETRANGEMAX, TRUE, 65535);
  429.  
  430.             if (vdd->lBalance<0)
  431.                 SendMessage(vdd->hwndBalance, TBM_SETPOS, TRUE, vdd->lBalance + 32768 - BALANCE_DEADZONE);
  432.             else if (vdd->lBalance>0)
  433.                 SendMessage(vdd->hwndBalance, TBM_SETPOS, TRUE, vdd->lBalance + 32768 + BALANCE_DEADZONE);
  434.             else
  435.                 SendMessage(vdd->hwndBalance, TBM_SETPOS, TRUE, 32768);
  436.  
  437.             SendMessage(vdd->hwndBalance, TBM_SETTIC, 0, 0);
  438.             SendMessage(vdd->hwndBalance, TBM_SETTIC, 0, 32768 - BALANCE_DEADZONE);
  439.             SendMessage(vdd->hwndBalance, TBM_SETTIC, 0, 32768 + BALANCE_DEADZONE);
  440.             SendMessage(vdd->hwndBalance, TBM_SETTIC, 0, 65536);
  441.  
  442.             EnableWindow(GetDlgItem(hdlg, IDC_STATIC_BALANCE), TRUE);
  443.         }
  444.     }
  445.  
  446.     _RPT0(0,"CaptureVumeterSetupMixer() end\n");
  447. }
  448.  
  449. BOOL APIENTRY CaptureVumeterDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam) {
  450.     VumeterDlgData *vdd = (VumeterDlgData *)GetWindowLong(hDlg, DWL_USER);
  451.     MMRESULT res;
  452.  
  453.     switch(message) {
  454.  
  455.         case WM_INITDIALOG:
  456.             try {
  457.                 HWND hWndLeft, hWndRight;
  458.                 LONG fsize;
  459.                 WAVEFORMATEX *wf;
  460.  
  461.                 ////
  462.  
  463.                 if (!(vdd = new VumeterDlgData)) throw MyError("Out of memory");
  464.                 memset(vdd, 0, sizeof(VumeterDlgData));
  465.  
  466.                 if (fsize = capGetAudioFormatSize((HWND)lParam)) {
  467.                     if (wf = (WAVEFORMATEX *)allocmem(fsize)) {
  468.                         if (capGetAudioFormat((HWND)lParam, wf, fsize)) {
  469.                             vdd->fStereo = wf->nChannels>1 ? TRUE : FALSE;
  470.                         }
  471.                         freemem(wf);
  472.                     }
  473.                 }
  474.  
  475.                 if (!(vdd->buffer = allocmem(vdd->fStereo?1024 + 2048 : 512 + 1024))) throw MyError("Out of memory");
  476.                 if (!(vdd->pfht_left = new Fht(1024, 11025))
  477.                     || (vdd->fStereo && !(vdd->pfht_right = new Fht(1024, 11025))))
  478.                     throw MyMemoryError();
  479.  
  480.                 vdd->scrollBuffer = (char *)vdd->buffer + (vdd->fStereo ? 1024 : 512);
  481.                 vdd->scrollSize = vdd->fStereo ? 2048 : 1024;
  482.  
  483.                 SetWindowLong(hDlg, DWL_USER, (DWORD)vdd);
  484.  
  485.                 GetWindowRect(hWndLeft = GetDlgItem(hDlg, IDC_VOLUME_LEFT), &vdd->rect[0]);
  486.                 GetWindowRect(hWndRight = GetDlgItem(hDlg, IDC_VOLUME_RIGHT), &vdd->rect[1]);
  487.  
  488.                 if (!(vdd->pbm = new MonoBitmap(NULL, (vdd->rect[0].right - vdd->rect[0].left), (vdd->rect[0].bottom - vdd->rect[0].top) + (vdd->rect[1].bottom - vdd->rect[1].top), RGB(0x00, 0x80, 0xff), RGB(0,0,0))))
  489.                     throw MyMemoryError();
  490.  
  491.                 ShowWindow(hWndLeft, SW_HIDE);
  492.                 ShowWindow(hWndRight, SW_HIDE);
  493.  
  494.                 ScreenToClient(hDlg, (LPPOINT)&vdd->rect[0] + 0);
  495.                 ScreenToClient(hDlg, (LPPOINT)&vdd->rect[0] + 1);
  496.                 ScreenToClient(hDlg, (LPPOINT)&vdd->rect[1] + 0);
  497.                 ScreenToClient(hDlg, (LPPOINT)&vdd->rect[1] + 1);
  498.  
  499.                 vdd->wfex.wFormatTag        = WAVE_FORMAT_PCM;
  500.                 vdd->wfex.nChannels            = vdd->fStereo?2:1;
  501.                 vdd->wfex.nSamplesPerSec    = 11025;
  502.                 vdd->wfex.nAvgBytesPerSec    = vdd->fStereo?22050:11025;
  503.                 vdd->wfex.nBlockAlign        = vdd->fStereo?2:1;
  504.                 vdd->wfex.wBitsPerSample    = 8;
  505.                 vdd->wfex.cbSize            = 0;
  506.  
  507.                 if (MMSYSERR_NOERROR != (res = waveInOpen(&vdd->hWaveIn, WAVE_MAPPER, &vdd->wfex, (DWORD)hDlg, 0, CALLBACK_WINDOW)))
  508.                     throw MyError("MM system: error %ld", res);
  509.  
  510.                 vdd->fOpened = TRUE;
  511.  
  512.                 for(vdd->bufCnt=0; vdd->bufCnt<2; vdd->bufCnt++) {
  513.                     vdd->bufs[vdd->bufCnt].lpData            = (char *)vdd->buffer + (vdd->fStereo?512:256)*vdd->bufCnt;
  514.                     vdd->bufs[vdd->bufCnt].dwBufferLength    = vdd->fStereo?512:256;
  515.                     vdd->bufs[vdd->bufCnt].dwFlags            = NULL;
  516.  
  517.                     if (MMSYSERR_NOERROR != (res = waveInPrepareHeader(vdd->hWaveIn,&vdd->bufs[vdd->bufCnt],sizeof(WAVEHDR))))
  518.                         throw MyError("MM system: error %ld", res);
  519.                     if (MMSYSERR_NOERROR != (res = waveInAddBuffer(vdd->hWaveIn,&vdd->bufs[vdd->bufCnt],sizeof(WAVEHDR))))
  520.                         throw MyError("MM system: error %ld", res);
  521.                 }
  522.  
  523.                 CaptureVumeterSetupMixer(hDlg, vdd);
  524.  
  525.                 if (MMSYSERR_NOERROR != (res = waveInStart(vdd->hWaveIn)))
  526.                     throw MyError("MM system: error %ld", res);
  527.  
  528.                 vdd->fRecording = TRUE;
  529.                 vdd->mode = VMMODE_VUMETER;
  530.  
  531.                 CheckDlgButton(hDlg, IDC_VMODE_VUMETER, BST_CHECKED);
  532.  
  533.             } catch(MyError e) {
  534.                 e.post(NULL,"Vumeter error");
  535.  
  536.                 if (vdd) CaptureVumeterDestruct(vdd);
  537.  
  538.                 EndDialog(hDlg, FALSE);
  539.                 return FALSE;
  540.             }
  541.  
  542.             return TRUE;
  543.  
  544.         case WM_PAINT:
  545.             {
  546.                 PAINTSTRUCT ps;
  547.                 HDC hDC;
  548.  
  549.                 hDC = BeginPaint(hDlg, &ps);
  550.  
  551.                 if (vdd->mode == VMMODE_VUMETER)
  552.                     CaptureVumeterRepaintVumeter(vdd, hDC);
  553.                 else
  554.                     CaptureVumeterRepaintScopeAnalyzer(vdd, hDC);
  555.  
  556.                 EndPaint(hDlg, &ps);
  557.             }
  558.             return TRUE;
  559.  
  560.         case WM_SYSCOMMAND:
  561.             if ((wParam & 0xFFF0) == SC_CONTEXTHELP) {
  562.                 HelpPopup(hDlg, IDH_CAPTURE_VUMETER);
  563.                 return TRUE;
  564.             }
  565.             break;
  566.  
  567.         case WM_COMMAND:
  568.             switch(LOWORD(wParam)) {
  569.  
  570.             case IDOK:
  571.             case IDCANCEL:
  572.                 if (vdd) CaptureVumeterDestruct(vdd);
  573.                 EndDialog(hDlg, FALSE);
  574.                 return TRUE;
  575.             case IDC_VMODE_VUMETER:
  576.                 vdd->mode = VMMODE_VUMETER;
  577.                 InvalidateRect(hDlg, &vdd->rect[0], FALSE);
  578.                 InvalidateRect(hDlg, &vdd->rect[1], FALSE);
  579.                 return TRUE;
  580.             case IDC_VMODE_OSCILLOSCOPE:
  581.                 vdd->mode = VMMODE_SCOPE;
  582.                 return TRUE;
  583.             case IDC_VMODE_ANALYZER:
  584.                 vdd->mode = VMMODE_ANALYZER;
  585.                 return TRUE;
  586.             }
  587.             break;
  588.  
  589.         case WM_HSCROLL:
  590.             if (!lParam) return FALSE;
  591.  
  592.             if ((HWND)lParam == vdd->hwndVolume || (HWND)lParam == vdd->hwndBalance) {
  593.                 if ((HWND)lParam == vdd->hwndVolume) {
  594.                     vdd->lVolume = SendMessage(vdd->hwndVolume, TBM_GETPOS, 0, 0);
  595.                 } else {
  596.                     long lPos;
  597.  
  598.                     lPos = SendMessage(vdd->hwndBalance, TBM_GETPOS, 0, 0) - 32768;
  599.  
  600.                     if (lPos < -BALANCE_DEADZONE)
  601.                         vdd->lBalance = lPos + BALANCE_DEADZONE;
  602.                     else if (lPos > BALANCE_DEADZONE)
  603.                         vdd->lBalance = lPos - BALANCE_DEADZONE;
  604.                     else
  605.                         vdd->lBalance = 0;
  606.                 }
  607.  
  608.                 // compute L/R volume
  609.  
  610.                 if (vdd->mcdVolume.cChannels == 1)
  611.                     vdd->mcdVolumeData[0].dwValue = vdd->lVolume;
  612.                 else if (vdd->lBalance < 0) {
  613.                     vdd->mcdVolumeData[0].dwValue = vdd->lVolume;
  614.                     vdd->mcdVolumeData[1].dwValue = MulDiv(vdd->lVolume, (32768-BALANCE_DEADZONE)+vdd->lBalance, 32768-BALANCE_DEADZONE);
  615.                 } else {
  616.                     vdd->mcdVolumeData[0].dwValue = MulDiv(vdd->lVolume, (32768-BALANCE_DEADZONE)-vdd->lBalance, 32768-BALANCE_DEADZONE);
  617.                     vdd->mcdVolumeData[1].dwValue =    vdd->lVolume;
  618.                 }
  619.                 mixerSetControlDetails(
  620.                         (HMIXEROBJ)vdd->hWaveIn,
  621.                         &vdd->mcdVolume,
  622.                         MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_HWAVEIN);
  623.             }
  624.             return TRUE;
  625.  
  626.         case MM_WIM_DATA:
  627.             {
  628.                 WAVEHDR *hdr = (WAVEHDR *)lParam;
  629.                 unsigned char c,*src = (unsigned char *)hdr->lpData;
  630.                 long dwBytes = hdr->dwBytesRecorded, len;
  631.  
  632.                 if (dwBytes) {
  633.                     memmove(vdd->scrollBuffer, vdd->scrollBuffer + dwBytes, vdd->scrollSize - dwBytes);
  634.                     memcpy(vdd->scrollBuffer + vdd->scrollSize - dwBytes, src, dwBytes);
  635.                 }
  636.  
  637.                 if (vdd->fStereo) {
  638.                     if (vdd->fOddByte) {
  639.                         ++src;
  640.                         --dwBytes;
  641.                     }
  642.  
  643.                     vdd->fOddByte = dwBytes & 1;
  644.  
  645.                     dwBytes/=2;
  646.                 }
  647.                 if (len = dwBytes) {
  648.                     unsigned long total[2]={0,0}, peak[2]={0,0}, v;
  649.                     HDC hdc;
  650.  
  651.                     if (!vdd->fStereo) {
  652.                         do {
  653.                             c = *src++;
  654.  
  655.                             if (c>=0x80)    v = (c-0x80);
  656.                             else            v = (0x80-c);
  657.  
  658.                             if (v>peak[0]) peak[0]=v;
  659.  
  660.                             total[0] += v;
  661.  
  662.                         } while(--len);
  663.                     } else {
  664.                         do {
  665.                             c = *src++;
  666.  
  667.                             if (c>=0x80)    v = (c-0x80);
  668.                             else            v = (0x80-c);
  669.  
  670.                             if (v>peak[0]) peak[0]=v;
  671.  
  672.                             total[0] += v;
  673.  
  674.                             //////
  675.  
  676.                             c = *src++;
  677.  
  678.                             if (c>=0x80)    v = (c-0x80);
  679.                             else            v = (0x80-c);
  680.  
  681.                             if (v>peak[1]) peak[1]=v;
  682.  
  683.                             total[1] += v;
  684.                         } while(--len);
  685.                     }
  686.  
  687.                     if (hdc = GetDC(hDlg)) {
  688.                         if (vdd->mode == VMMODE_VUMETER)
  689.                             CaptureVumeterDoVumeter(vdd, hdc, total, peak, dwBytes);
  690.                         else if (vdd->mode == VMMODE_ANALYZER)
  691.                             CaptureVumeterDoAnalyzer(vdd, hdc);
  692.                         else
  693.                             CaptureVumeterDoScope(vdd, hdc);
  694.                         ReleaseDC(hDlg, hdc);
  695.                     }
  696.                 }
  697.  
  698.                 hdr->dwFlags &= ~WHDR_DONE;
  699.  
  700. //                _RPT1(0,"Received buffer: %ld bytes\n", hdr->dwBytesRecorded);
  701.  
  702.                 if (MMSYSERR_NOERROR != (res = waveInAddBuffer(vdd->hWaveIn,hdr,sizeof(WAVEHDR))))
  703.                     MyError("MM system: error %ld",res).post(hDlg,"Vumeter error");
  704.             }
  705.             break;
  706.     }
  707.  
  708.     return FALSE;
  709. }
  710.